/**
 * 
 */
package fhq.hcmute.edu.vn.network;

import java.awt.Component;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.security.PublicKey;

import javax.swing.JOptionPane;

import fhq.hcmute.edu.vn.config.ListDevicesConnected;
import fhq.hcmute.edu.vn.event.Command;
import fhq.hcmute.edu.vn.gui.ConfirmDialog;
import fhq.hcmute.edu.vn.gui.MySystemTray;
import fhq.hcmute.edu.vn.gui.PictureFrame;
import fhq.hcmute.edu.vn.keyboard.KeyCode;
import fhq.hcmute.edu.vn.message.MessageFactory;
import fhq.hcmute.edu.vn.message.MyMessage;
import fhq.hcmute.edu.vn.security.KeyManagement;
import fhq.hcmute.edu.vn.system.MyFolder;
import fhq.hcmute.edu.vn.util.ProjectUtil;

/**
 * @author nglehien Nov 5, 2012 - 4:20:41 PM
 */
public class Connection extends Thread {
	private Socket socket;
	private ObjectInputStream ois;
	private ObjectOutputStream ous;
	private ComputerInfos serverInfo;
	private Component component;
	private boolean isRunning = true;
	private Server server;

	public boolean isRunning() {
		return isRunning;
	}

	public void setRunning(boolean isRunning) {
		this.isRunning = isRunning;
	}

	public ComputerInfos getServerInfo() {
		return serverInfo;
	}

	public void setServerInfo(ComputerInfos serverInfo) {
		this.serverInfo = serverInfo;
	}

	public Component getComponent() {
		return component;
	}

	public void setComponent(Component component) {
		this.component = component;
	}

	public Connection(Socket socket, Server server) {
		this.socket = socket;
		this.server = server;
		start();
	}

	@Override
	public void run() {
		try {
			if (isAllowClientToConnect()) {
				MyMessage message;
				while (isRunning) {
					if ((message = (MyMessage) inFromClient()) != null) {
						handleMessage(message);
					}
				}
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		super.run();
	}

	private void handleMessage(MyMessage message) throws IOException {
		Object[] data = message.getData();
		switch (message.getType()) {
		case MessageFactory.PING:
			outToClient(MessageFactory.createReturnMessage(MessageFactory.PING,
					true));
			break;
		case MessageFactory.STOP_SERVER:
			isRunning = false;
			MySystemTray.getInstace().reset();
			closeClientConnection();
			server.stop();
			break;
		case MessageFactory.MOVE:
			Command.mouseMove(data);
			break;
		case MessageFactory.MOUSE_DROP:
			Command.mouseDrop();
			break;
		case MessageFactory.MOUSE_DRAP:
			Command.mouseDrap();
			break;
		case MessageFactory.MOUSE_CLICK_LEFT:
			Command.mouseLeftClick();
			break;
		case MessageFactory.MOUSE_CLICK_RIGHT:
			Command.mouseRightClick();
			break;
		case MessageFactory.MOUSE_DOUBLE_CLICK:
			Command.mouseDoubleClick();
			break;
		case MessageFactory.MOUSE_SCROLL_DOWN:
			Command.mouseScrollDown(data);
			break;
		case MessageFactory.MOUSE_SCROLL_UP:
			Command.mouseScrollUp(data);
			break;
		case MessageFactory.KEYBOARD_PRESS:
			KeyCode keyCode = (KeyCode) message.getData()[1];
			keyCode = KeyCode.fromString(keyCode.name(), KeyCode.TELEX);
			if (keyCode.getKeyCount() > 0) {
				Command.keyboardPress(keyCode);
			}
			break;
		case MessageFactory.KEYBOARD_PRESS_LEFT:
			Command.keyboardLeftArrow(data);
			break;
		case MessageFactory.KEYBOARD_PRESS_RIGHT:
			Command.keyboardRightArrow(data);
			break;
		case MessageFactory.ZOOM_IN:
			Command.zoomIn();
			break;
		case MessageFactory.ZOOM_OUT:
			Command.zoomOut();
			break;
		case MessageFactory.CHANGE_DIRECTORY:
			outToClient(MessageFactory.createReturnMessage(
					MessageFactory.CHANGE_DIRECTORY,
					Command.changeDirectory(data)));
			break;
		case MessageFactory.UP_TO_ROOT:
			outToClient(MessageFactory.createReturnMessage(
					MessageFactory.UP_TO_ROOT, Command.upToRoot()));
			break;
		case MessageFactory.EXECUTE_FILE:
			if (Command.Excute(data)) {
				outToClient(MessageFactory.createReturnMessage(
						MessageFactory.EXECUTE_FILE, true));
			} else {
				outToClient(MessageFactory.createReturnMessage(
						MessageFactory.EXECUTE_FILE, true));
			}
			break;
		case MessageFactory.REFRESH:
			outToClient(MessageFactory.createReturnMessage(
					MessageFactory.REFRESH, Command.refresh()));
			break;
		case MessageFactory.TURN_BACK:
			MyFolder folder = Command.turnBack();
			if (folder == null) {
				folder = Command.upToRoot();
			}
			outToClient(MessageFactory.createReturnMessage(
					MessageFactory.TURN_BACK, folder));
			break;
		case MessageFactory.PWD:
			outToClient(MessageFactory.createReturnMessage(MessageFactory.PWD,
					Command.pwd()));
			break;
		case MessageFactory.SEARCH:
			outToClient(MessageFactory.createReturnMessage(
					MessageFactory.SEARCH, Command.search(data)));
			break;
		case MessageFactory.POST_IMAGE:
			outToClient(MessageFactory.createReturnMessage(
					MessageFactory.POST_IMAGE, Command.postImage(data)));

			break;
		case MessageFactory.CLOSE_IMAGE_FRAME:
			PictureFrame frame = PictureFrame.getInstance();
			frame.dispose();
			break;
		case MessageFactory.ROTATE_LEFT:
			PictureFrame.getInstance().rotateLeft();
			outToClient(MessageFactory.createReturnMessage(
					MessageFactory.ROTATE_LEFT, true));
			break;
		case MessageFactory.ROTATE_RIGHT:
			PictureFrame.getInstance().rotateRight();
			outToClient(MessageFactory.createReturnMessage(
					MessageFactory.ROTATE_RIGHT, true));
			break;
		case MessageFactory.REVIEW_IMAGE:
			outToClient(MessageFactory.createReturnMessage(
					MessageFactory.REVIEW_IMAGE, Command.reviewImage(data)));
			break;
		case MessageFactory.REMOVE_IMAGE:
			outToClient(MessageFactory.createReturnMessage(
					MessageFactory.REVIEW_IMAGE, Command.removeImage(data)));
			break;
		case MessageFactory.REMOVE_ALL_IMAGE:
			outToClient(MessageFactory.createReturnMessage(
					MessageFactory.REVIEW_IMAGE, Command.removeAllImages()));
			break;
		case MessageFactory.PREZI_BACK:
			Command.preziPreviousSlide();
			break;
		case MessageFactory.PREZI_NEXT:
			Command.preziNextSlide();
			break;
		case MessageFactory.PREZI_ENTER_FULL_SCREEN:
			Command.preziEnterFullScreen();
			break;
		case MessageFactory.PREZI_EXIT_FULL_SCREEN:
			Command.preziExitFullScreen();
			break;
		case MessageFactory.PREZI_ZOOM_IN:
			Command.preziZoomIn();
			break;
		case MessageFactory.PREZI_ZOOM_OUT:
			Command.preziZoomOut();
			break;
		case MessageFactory.PREZI_BLACKOUT:
			Command.preziBlackout();
			break;
		}

	}

	public final MyMessage inFromClient() throws ClassNotFoundException,
			IOException, SocketException {
		if (socket.isConnected()) {
			ois = new ObjectInputStream(socket.getInputStream());
			return (MyMessage) ois.readObject();
		}
		return null;
	}

	public final void outToClient(final MyMessage message) throws IOException {
		if (socket.isConnected()) {
			ous = new ObjectOutputStream(socket.getOutputStream());
			ous.writeObject(message);
		}
	}

	private boolean isAllowClientToConnect() throws Exception {
		boolean isAllow = false;
		MyMessage message = inFromClient();
		if (message.getType() == MessageFactory.AUTHENTICATION) {
			if (message.getFirstData() instanceof PhoneInfos) {
				PhoneInfos clientInfo = (PhoneInfos) message.getFirstData();
				if (clientInfo.getPassword().equals(serverInfo.getPassword())) {
					if (!ListDevicesConnected.isExists(clientInfo)) {
						return showConnectDialog(clientInfo);
					} else {
						byte[] decBytes = (byte[]) message.get(1);
						KeyManagement keyManagement = KeyManagement
								.getInstance();
						isAllow = keyManagement.compare(
								clientInfo.getPhoneIMEI(), decBytes,
								clientInfo.toString());
						allowToConnect();
					}
				}
			}
		}
		return isAllow;
	}

	private boolean showConnectDialog(final PhoneInfos clientInfo)
			throws IOException {
		boolean isAllowToConnect = false;
		ConfirmDialog confirmDialog = new ConfirmDialog(
				JOptionPane.getFrameForComponent(component), true);
		confirmDialog.setPhoneInfos(clientInfo);
		confirmDialog.setVisible(true);
		if (confirmDialog.getDialogResult() == ConfirmDialog.ACCEPT_REMEMBER) {
			isAllowToConnect = true;
			PublicKey publicKey = confirmDialog.getPublicKey();
			allowToConnect(publicKey);
			isAllowToConnect = true;
		} else if (confirmDialog.getDialogResult() == ConfirmDialog.ACCEPT) {
			allowToConnect();
			isAllowToConnect = true;
		} else {
			MyMessage message = MessageFactory
					.createPermissionMessage(ProjectUtil.NO);
			outToClient(message);
			closeClientConnection();
			isAllowToConnect = false;
		}
		return isAllowToConnect;
	}

	private void allowToConnect() throws IOException {
		MySystemTray mySystemTray = MySystemTray.getInstace();
		mySystemTray.setMinimize();
		MyMessage message = MessageFactory
				.createPermissionMessage(ProjectUtil.YES);
		outToClient(message);
	}

	private void allowToConnect(PublicKey privateKey) throws IOException {
		MySystemTray mySystemTray = MySystemTray.getInstace();
		mySystemTray.setMinimize();
		MyMessage message = MessageFactory.createPermissionMessage(
				ProjectUtil.YES, privateKey);
		outToClient(message);
	}

	public final boolean closeClientConnection() throws IOException {
		if (socket.isConnected()) {
			ois.close();
			ous.close();
			serverInfo = null;
			component = null;
			isRunning = false;
			socket.close();
			return true;
		}
		return false;
	}
}
